package com.nema.infer;


import com.nema.util.*;
import lsh.LshPackage.Pair;
import org.neo4j.driver.v1.Record;
import org.neo4j.driver.v1.StatementResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import software.lib.util.NeoDriver;

import javax.sql.DataSource;
import java.io.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;


/**
 * @author sunfiyes 2016年12月12日
 *
 */
@Service
public class NemaInfer {
	public double THRESHHOLD = 0.5;
	public double LAMDA = 0.3;
	public int LOOP = 3;
	public double MAXNUM = Double.MAX_VALUE;
	public Map<String, List<String>> M = new HashMap<String, List<String>>();
	public Map<String, Map<String, Double>> U0 = new HashMap<String, Map<String, Double>>();
	public Map<String, String>[] OPT = new Map[LOOP+1];//匹配结果
	
	NeoDriver neodriver = new NeoDriver();
	public TargetGraph nbv = new TargetGraph();
	public UserInput nbvq = new UserInput();
	public Map<String, Node> nodeMap;
	public Map<String, Map<String, Double>> neighborVec;
	public Map<String, Node> nodeMapQ;
	public Map<String, Map<String, Double>> neighborVecQ;
	
	Map<String, Double> Gig_Sim = new HashMap<String, Double>();

	public NemaInfer(Map<String, String> input,Pair[] pairs){
		this.nodeMap = nbv.createNode();
		this.neighborVec = nbv.createNeighborVec();
		this.nodeMapQ = nbvq.createNode(input);
		this.neighborVecQ = nbvq.createNeighborVec();
		initCandidate(pairs);
		initInferCost();
	}
	
	public void inference(){//主函数
		Map<String, Map<String, Double>>[] U = new Map[LOOP+1];
		U[0] = U0;	
		for(int loop = 1; loop <= LOOP; loop++){
			U[loop] = new HashMap<String, Map<String, Double>>();
			OPT[loop] = new HashMap<String, String>();
			Iterator i = nodeMapQ.entrySet().iterator();
			while (i.hasNext()) {
				double opt_cost = MAXNUM;
				Entry entry = (Entry) i.next();
				String v = (String)entry.getKey();
				List<String> Mv = M.get(v);
				Map<String, Double> v_cost = new HashMap<String, Double>();
				for(String u : Mv){//候选集
					double W = calc_W(U[loop-1], v, u);
					double cost = W + 1 - delta(v, u);
					v_cost.put(u, cost);
					if(opt_cost > cost){
						opt_cost = cost;
						OPT[loop].put(v, u);
					}
					//System.out.println(W);
				}
				U[loop].put(v, v_cost);
			}
		}
	}


	public Map<String, List<String>> initCandidate(Pair[] pairs){//初始化候选集
		Iterator i = nodeMapQ.entrySet().iterator();
		while (i.hasNext()) {
			Entry entry = (Entry) i.next();
			Node v = (Node)entry.getValue();
			if(v.getType().equals("gig")){
				M.put((String)entry.getKey(), new ArrayList<String>());
				continue;
			}
			String n1 = (String)entry.getKey();
			Iterator j = nodeMap.entrySet().iterator();
			List<String> cdd = new ArrayList<String>();
			while (j.hasNext()) {
				Entry _entry = (Entry) j.next();
				if(delta(n1, (String)_entry.getKey()) >= THRESHHOLD){
					cdd.add((String)_entry.getKey());
				}
			}
			M.put((String)entry.getKey(), cdd);
		}

		Arrays.stream(pairs)
				.peek(pair -> Gig_Sim.put(""+pair.nid, pair.similarity))
				.filter(pair -> pair.similarity>=0.3).forEach(pair -> {
			M.get(1).add(""+pair.nid);
		});
		return M;
	}

	/*public Map<String, List<String>> initCandidate(){//初始化候选集
		Iterator i = nodeMapQ.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry entry = (Map.Entry) i.next();
			String n1 = (String)entry.getKey();
			Iterator j = nodeMap.entrySet().iterator();
			List<String> cdd = new ArrayList<String>();
			while (j.hasNext()) {
				Map.Entry _entry = (Map.Entry) j.next();
				if(delta(n1, (String)_entry.getKey()) >= THRESHHOLD){
					cdd.add((String)_entry.getKey());
				}
			}
			M.put((String)entry.getKey(), cdd);
		}
		return M;
	}*/

	public Map<String, Map<String, Double>> initInferCost(){//初始化推倒代价
		double max = 0, sum1 = 0, sum2 = 0;
		Iterator i = nodeMapQ.entrySet().iterator();
		while (i.hasNext()) {
			Entry entry = (Entry) i.next();
			String v = (String)entry.getKey();
			List<String> Mv = M.get(entry.getKey());
			Set<String> ngb = neighborVecQ.get(v).keySet();
			Map<String, Double> v_cost = new HashMap<String, Double>();
			for(String u : Mv){//候选集
				sum1 = 0; sum2 = 0;
				for(String v1 : ngb){//邻居节点
					max = 0;
					for(String u1 : M.get(v1)){//v1候选集
						if(distanceT(u, u1) > max){
							max = distanceT(u, u1);
						}
					}
					sum1 += delta_plus(distanceQ(v, v1), max);
					sum2 += distanceQ(v, v1);
				}
				double Fi = LAMDA * (1-delta(v,u)) + (1-LAMDA) * sum1 / sum2;
				v_cost.put(u, Fi);
			}
			U0.put(v, v_cost);
		}
		return U0;
	}

	public double calc_W(Map<String, Map<String, Double>> U, String v, String u){//partial inference cost for (v,u)
		Set<String> ngb = neighborVecQ.get(v).keySet();
		double min = MAXNUM, sum = 0, tmp = 0;
		double beta = beta_v(v);
		for(String v1 : ngb){//v邻居节点
			min = MAXNUM;
			for(String u1 : M.get(v1)){//v1候选集
				tmp = delta_plus(distanceQ(v, v1),distanceT(u, u1)) * beta + U.get(v1).get(u1);
				min = min > tmp ? tmp : min;
			}
			sum += min;
		}
		return sum = sum > 1 ? Math.log(sum) : sum;
	}

	public double delta(String v, String u){//节点相似度
		if(u==null)
			return 0;
		String type_v = nodeMapQ.get(v).type;
		String type_u = nodeMap.get(u).type;
		if( !nodeMapQ.get(v).type.equals(nodeMap.get(u).type) )
			return 0;
		String label_v = nodeMapQ.get(v).label;
		String label_u = nodeMap.get(u).label;
		if(type_v.equals("seller")){//
			return 1;
		}
		else if(type_v.equals("gig")){
			return Gig_Sim.containsKey(u) ? Gig_Sim.get(u): 0;
		}
		else if(type_v.equals("score")){//分数：1-10
			double sim = THRESHHOLD + (Double.parseDouble(label_u) - 75)/1000;
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("timespan")){//创建时长：单位 年
			double sim = THRESHHOLD + 0.001*(Double.parseDouble(label_u) - Double.parseDouble(label_v));
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("duration")){//交付时间:单位 天
			double sim = THRESHHOLD + 0.01*(Double.parseDouble(label_v) - Double.parseDouble(label_u));
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("price")){//价格
			double sim = THRESHHOLD + 0.01*(Double.parseDouble(label_v) - Double.parseDouble(label_u.split("-")[1]));
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("sale_effiency")){//销售效率
			double sim = THRESHHOLD + (Double.parseDouble(label_u) - 5)/100;
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("country")){//国家
			return label_u.contains(label_v) ? THRESHHOLD : 0;
		}
		else if(type_v.equals("language")){//语言
			return label_u.contains(label_v) ? THRESHHOLD : 0;
		}
		else if(type_v.equals("education")){//文化程度
			return !label_u.equals("") ? THRESHHOLD : 0;
		}
		else if(type_v.equals("certification")){//证书
			return !label_u.equals("") ? THRESHHOLD : 0;
		}
		else if(type_v.equals("level")){//等级
			return Integer.parseInt(label_u) > 1 ? THRESHHOLD : 0;
		}
		else if(type_v.equals("avg_resp_time")){//回复时间
			double sim = THRESHHOLD + 0.01*(Double.parseDouble(label_v) - Double.parseDouble(label_u));
			return sim < 0.7 ? sim : 0.7;
		}
		else if(type_v.equals("tag")){//关键字
			int cnt = -1;
			for(String key : label_v.split(";")){
				if(label_u.contains(key))
					cnt++;
			}
			double sim = THRESHHOLD + cnt*0.07;
			return sim < 0.7 ? sim : 0.7;
		}
		return 0;
	}

	public double distanceT(String n1, String n2){//T节点距离(相关度)
		if(n1==null)
			return 0;
		if(neighborVec.get(n1).containsKey(n2))
			return neighborVec.get(n1).get(n2);
		return 0;
	}

	public double distanceQ(String n1, String n2){//Q节点距离(相关度)
		if(neighborVecQ.get(n1).containsKey(n2))
			return neighborVecQ.get(n1).get(n2);
		return 0;
	}

	public double delta_plus(double x, double y){
		return x > y ? (x-y) : 0;
	}

	public double beta_v(String v){
		double sum = 0;
		Iterator i = neighborVecQ.get(v).entrySet().iterator();
		while (i.hasNext()) {
			Entry entry = (Entry) i.next();
			sum += (double)entry.getValue();
		}
		return 1/sum;
	}


	/*public void printOPT(){//打印最优匹配
		Iterator i = OPT[LOOP].entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry entry = (Map.Entry) i.next();
			String v = entry.getKey().toString();
			String u = entry.getValue().toString();
			System.out.println(nodeMapQ.get(v).getType()+"-->"+nodeMap.get(u).getLabel());
		}
	}*/

	public String printOPT(){//打印最优匹配
		String out = "";
		Set<String> hs = new HashSet<String>();
		Iterator i = OPT[LOOP].entrySet().iterator();
		while (i.hasNext()) {
			Entry entry = (Entry) i.next();
			String u = entry.getValue().toString();
			String type = nodeMap.get(u).type;
			StatementResult result;
			if(type.equals("gig")){
				result = neodriver.query( "start n=node("+u+")  MATCH (n) RETURN n.id As id");
			}else{
				result = neodriver.query( "start n=node("+u+")  MATCH (n)-[*]-(x:gig) RETURN x.id As id");
			}
			while ( result.hasNext() ){
				Record record = result.next();
				String gid = record.get("id").asString();
				hs.add(gid);
			}
		}
		return sortByScore(hs)+"-"+sortByCost(hs);
	}

	public String sortByCost(Set<String> GID){ // 按匹配代价排序
		Map<String, String> tmpMapQ = new HashMap<String, String>();
		Map<String, String> tmpMapT = new HashMap<String, String>();
		Map<String, Double> Cost = new HashMap<String, Double>();
		String out = "";
		StatementResult result;
		Record record = null;
		double sum1 = 0, sum2 = 0, Fi = 0;

		Iterator i = nodeMapQ.entrySet().iterator();
		while (i.hasNext()) {
			Entry entry = (Entry) i.next();
			tmpMapQ.put(((Node)entry.getValue()).type, (String)entry.getKey());
		}

		for(String gid : GID){
			result = neodriver.query( "MATCH (n:gig)-[]-(x) WHERE n.id='"+gid+"' RETURN ID(n) As idn, ID(x) As id, Labels(x) As type");
			tmpMapT = new HashMap<String, String>();
			Fi = 0;
			while(result.hasNext()){
				record = result.next();
				String id = record.get("id").toString();
				String type = record.get("type").get(0).asString();
				tmpMapT.put(type, id);
				if(type.equals("seller")){
					StatementResult result_ = neodriver.query( "start n=Node("+id+") MATCH (n)-[]-(x) RETURN ID(x) As id, Labels(x) As type");
					while(result_.hasNext()){
						Record record_ = result_.next();
						String type_ = record_.get("type").get(0).asString();
						if(!type_.equals("gig"))
							tmpMapT.put(type_, record_.get("id").toString());
					}
				}
			}
			tmpMapT.put("gig", record.get("idn").toString());

			for (Entry<String, String> entry : tmpMapQ.entrySet()) {//查询图
				String v = (String)entry.getValue();
				String type = (String)entry.getKey();
				for (Entry<String, Double> entry_ : neighborVecQ.get(v).entrySet()) {//查询节点的邻居
					String v1 = entry_.getKey();
					String type1 = nodeMapQ.get(v1).type;
					sum1 += delta_plus(entry_.getValue(), distanceT(tmpMapT.get(type), tmpMapT.get(type1)));
					sum2 += entry_.getValue();
				}
				Fi += 0.3 * (1-delta(v,tmpMapT.get(type))) + 0.7 * sum1 / sum2;
			}
			Cost.put(gid, Fi);
		}

		List<Entry<String, Double>> list = new ArrayList<Entry<String, Double>>(Cost.entrySet());
        Collections.sort(list,new Comparator<Entry<String,Double>>() {
            //降序排序
            public int compare(Entry<String, Double> o1, Entry<String, Double> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        for (Entry<String, Double> e: list) {
        	out += ";"+e.getKey();
        }

        neodriver.close();
        return out.substring(1);
	}

	public String sortByScore(Set<String> GID){ // 按得分排序
		String out = "";
		Map<String, Integer> Score = new TreeMap<String, Integer>();
		try(Connection conn = dataSource.getConnection()){
			for(String gid : GID){
				ResultSet ret = conn.prepareStatement("select score from gig where gig_id='"+gid+"'").executeQuery();
				if(ret.next()){
					Score.put(gid, ret.getInt(1));
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

		List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(Score.entrySet());
        Collections.sort(list,new Comparator<Entry<String,Integer>>() {
            //降序排序  
            public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {  
                return o2.getValue().compareTo(o1.getValue());  
            }  
        });  
        for (Entry<String, Integer> e: list) {  
        	out += ";"+e.getKey();
        }
        return out.substring(1);
	}

	@Autowired
	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
}















